package org.reborn.engine.render.texture

import org.reborn.utils.niobuffer.BufferUtil
import org.reborn.utils.FileUtil

import java.nio.{Buffer,ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,FloatBuffer,LongBuffer,DoubleBuffer}

import javax.media.opengl.{GL,GL2,GL2GL3,GL2ES2}
import javax.imageio.ImageIO
import java.awt.image.{BufferedImage,Raster}
import java.awt.Image

import TextureType._

trait TextureManagerCubeMapSupport {
  self:CachingTextureManager =>
	
  private val TEST_TEX_CubeMap = "Test Texture CubeMap";
  
  private implicit def defaultTextureCubeMap(gl:GL):TextureCubeMap = TestTextureCubeMap(gl)
  
  /**
   * Create a TextureCubeMap texture, using the default texture metadata.
   * @param gl the GL object so that JOGL operations can be performed
   * @param textureData a Seq of java.nio.Buffer objects that contain the image data. The order of
   * cube map faces needs to be Negative X, Positive X, Negative Y, Positive Y, Negative Z, Positive Z 
   * @param width width of the texture data in pixels
   * @param height height of the texture data in pixels
   * @param glFormat the format of the texture data (GL_RGB, GL_RGBA, GL_LUMINENCE, etc...)
   * @param glType the type of data in the textureData buffer (GL_UNSIGNED_BYTE, GL_INT, GL_FLOAT, etc...)
   * This type must correspond to the type of Buffer implementation used by textureData
   * @return the TextureCubeMap object
   */
  def TextureCubeMap(gl:GL,textureData:Seq[Buffer],width:Int,height:Int,glFormat:Int,glType:Int):TextureCubeMap = {
	val metadata = defaultCubeMapMetadata
    metadata.width = width
    metadata.height = height
    
    return TextureCubeMap(gl,textureData,metadata,glFormat,glType)
  }
  
  /**
   * Create a TextureCubeMap texture. 
   * @param gl the GL object so that JOGL operations can be performed
   * @param textureData an array of java.nio.Buffer objects that contain the image data. The order of
   * cube map faces needs to be Negative X, Positive X, Negative Y, Positive Y, Negative Z, Positive Z 
   * @param textureMetadata the metadata of the texture (width, height, internal format, etc...)
   * @param glFormat the format of the texture data (GL_RGB, GL_RGBA, GL_LUMINENCE, etc...)
   * @param glType the type of data in the textureData buffer (GL_UNSIGNED_BYTE, GL_INT, GL_FLOAT, etc...)
   * This type must correspond to the type of Buffer implementation used by textureData
   * @return the TextureCubeMap object
   */
  def TextureCubeMap(gl:GL,textureData:Seq[Buffer],textureMetadata:TextureCubeMapMetadata,glFormat:Int,glType:Int):TextureCubeMap = {
	require(textureData.size % 6 == 0,"You must specify 6 buffers to create a Cube Map.")
	
	val textureKey = (getNewBufferFileName,TexCubeMap)
    val tex:TextureCubeMap = addTextureToMap(gl,textureKey){()=>
      Some(createTextureCubeMapFromBuffer(gl,textureData,textureMetadata,glFormat,glType))
    }
    
    tex
  }
  
  /**
   * Load an image and create a TextureCubeMap object
   * @param gl the GL object so that JOGL operations can be performed
   * @param fileNames The names of the image files to load. Must be in the 
   * following order: Negative X, Positive X, Negative Y, Positive Y, Negative Z, Positive Z
   * @param textureMetadata the metadata of the texture (internal format, etc...). The 
   * width and height attributes of the metadata will be overwritten by the actual width
   * and height of the image file.
   * @return the TextureCubeMap object
   */
  def TextureCubeMap(gl:GL,fileNames:Seq[String],textureMetadata:TextureCubeMapMetadata):TextureCubeMap = {
	val textureKey = (fileNames(0),TexCubeMap)
	
	val tex:TextureCubeMap = addTextureToMap(gl,textureKey){()=>
	  val files:Seq[Option[(Int,Int,Int,Int,Buffer)]] = fileNames.map{(fileName)=>
	    getImgDataFromFile(fileName)
	  }
	  
	  if(files.forall(_.isDefined)){
	 	val (width,height,glFormat,glType,buffer) = files(0).get
	 	textureMetadata.width = width
	    textureMetadata.height = height
	    textureMetadata.glInternalFormat = glFormat
	    Some(createTextureCubeMapFromBuffer(gl,files.map(_.get._5),textureMetadata,glFormat,glType))
	  }
	  else{
	    System.err.println("Could not find one of the specified Cube Map Image, " +
	    		"or the images were not in a supported format: "+fileNames(0))
	    None
	  }
	}
	
	tex
  }
  
  /**
   * Load an image and create a TextureCubeMap object using the default metadata
   * @param gl the GL object so that JOGL operations can be performed
   * @param fileNames The names of the image files to load. Must be in the 
   * following order: Negative X, Positive X, Negative Y, Positive Y, Negative Z, Positive Z
   * @return the TextureCubeMap object
   */
  def TextureCubeMap(gl:GL,fileNames:Seq[String]):TextureCubeMap = {
	val metadata = defaultCubeMapMetadata
	TextureCubeMap(gl,fileNames,metadata)
  }
  
  /**
   * Creates a test texture CubeMap object. This texture is also returned if 
   * there is an error loading a CubeMap texture from a file
   * @param gl the GL object so that JOGL operations can be performed
   */
  def TestTextureCubeMap(gl:GL):TextureCubeMap = {
	val textureKey = (TEST_TEX_CubeMap,TexCubeMap)
	
	val tex:TextureCubeMap = addTextureToMap(gl,textureKey){()=>
	  val metadata = new TextureCubeMapMetadata
	  metadata.width=2
	  metadata.height=2
	  metadata.magFilter = TextureFilter.Nearest
	  metadata.minFilter = TextureFilter.Nearest
	  metadata.glInternalFormat = GL.GL_RGBA 
	
	  val image = testTextureBuffer
	  
	  val images = Array(image,image,image,image,image,image)
		
	  Some(createTextureCubeMapFromBuffer(gl,images,metadata,GL.GL_RGBA,GL.GL_UNSIGNED_BYTE))
	}
	tex
  }
  
  /**
   * Creates a TextureCubeMapMetadata object with the current TextureManager 
   * metadata values. This metadata object will also be used for any
   * texture creation operation where metadata is not explicitly passed
   * as a parameter.
   */
  def defaultCubeMapMetadata:TextureCubeMapMetadata = {
	val metadata = new TextureCubeMapMetadata
	updateCommonMetadata(metadata)
	return metadata
  }
  
  protected def createTextureCubeMapFromBuffer(gl:GL,textureData:Seq[Buffer],textureMetadata:TextureCubeMapMetadata,glFormat:Int,glType:Int):TextureCubeMap = {
    val text = new TextureCubeMap(textureMetadata)
    text.createTexture(gl,textureData,glFormat,glType)
    return text
  }
}